
PLUG.Platform = PLUG_PLATFORM_SERVER

PLUG.ClassName = "sv_grps"
PLUG.PrintName = "Serverside User-Groups"
PLUG.Author = "thomasfn"

PLUG.Hard = true

local GrpRoot = "gmanage/ugrps/"

function PLUG:Init()
	plugs.Call( "LoadUsergroups" )
end

function PLUG:PlayerExtend( meta )
	function meta:HasPriv( priv )
		local ps = (self._DAT or {}).Privs or {}
		if (table.HasValue( ps, "none" )) then return end
		return table.HasValue( ps, "all" ) or table.HasValue( ps, priv )
	end
	function meta:GivePriv( priv )
		if (self:HasPriv( priv )) then return end
		self._DAT = self._DAT or {}
		self._DAT.Privs = self._DAT.Privs or {}
		table.insert( self._DAT.Privs, priv )
		self:SavePlayerData()
	end
	function meta:TakePriv( priv )
		if (!self:HasPriv( priv )) then return end
		self._DAT = self._DAT or {}
		self._DAT.Privs = self._DAT.Privs or {}
		for k, v in pairs( self._DAT.Privs ) do
			if (v == priv) then
				table.remove( self._DAT.Privs, k )
				return
			end
		end
	end
	function meta:BeamPrivs( to )
		to = to or self
		datastream.StreamToClients( to, "gmanage", { "playerprivs", self:GetGID(), self._DAT.Privs } )
	end
	function meta:BeamUGS( to, which )
		to = to or self
		local ugs = plugs.Call( "GetUGS" )
		if (which) then
			datastream.StreamToClients( to, "gmanage", { "ugdat", which, ugs[ which ] } )
		else
			datastream.StreamToClients( to, "gmanage", { "ugs", ugs, plugs.Call( "GetServerValue", "DEF_UG", "restricted" ) } )
		end
	end
	function meta:SetUsergroup( ugname )
		if (!ugname) then
			local defgrp = plugs.Call( "GetDefaultUsergroup" )
			if (!defgrp) then
				Error( "Could not find default usergroup!" )
				return
			end
			ugname = defgrp
		end
		local dat = plugs.Call( "GetUsergroupData", ugname )
		if (!dat) then return end
		if (!self._DAT) then self._DAT = {} end
		self._DAT.Privs = dat.Privs
		self._DAT.UG = ugname
		self:SavePlayerData()
		self:SetNWString( "usr_group", ugname )
		self:BeamPrivs()
		if (self:HasPriv( "status_admin" )) then self:SetUserGroup( "admin" ) end
		if (self:HasPriv( "status_superadmin" )) then self:SetUserGroup( "superadmin" ) end
		self.UGTAB = dat
	end
	function meta:GetUsergroupTable()
		return self.UGTAB or {}
	end
	function meta:GetRank()
		local ugname = self._DAT.UG
		local dat = plugs.Call( "GetUsergroupData", ugname )
		if (!dat) then return 1 end
		return dat.Rank or 1
	end
	function meta:CanDoActionOn( ply )
		return (self == ply) || (self:GetRank() > ply:GetRank())
	end
	function meta:CanUseEntity( entclass )
		local ugname = self._DAT.UG
		if (!ugname) then return false end
		local dat = plugs.Call( "GetUsergroupData", ugname )
		if (!dat) then return false end
		return (!table.HasValue( dat.Rstrict or {}, "@ALL" )) && (!table.HasValue( dat.Rstrict or {}, entclass ))
	end
	
	meta.OldPrintMessage = meta.OldPrintMessage or meta.PrintMessage
	
	function meta:PrintMessage( where, msg )
		if (where == HUD_PRINTTALK) then
			umsg.Start( "gmanage", self )
				umsg.String( "message" )
				umsg.String( msg )
			umsg.End()
		end
		self:OldPrintMessage( where, msg )
	end
	
end

function PLUG:LoadUsergroups()
	self.UGS = {}
	self:LoadHardGroups()
	local dat = plugs.Call( "ReadUsergroups" )
	if (dat) then table.Merge( self.UGS, dat ) end
end

function PLUG:LoadHardGroups()
	local grp
	
	grp = {}
	grp.ClassName = "restricted"
	grp.PrintName = "Restricted"
	grp.Privs = {}
	grp.Hard = true
	grp.Rank = 0
	grp.Rstrict = { "@ALL" }
	self.UGS[ grp.ClassName ] = grp
	
	grp = {}
	grp.ClassName = "owner"
	grp.PrintName = "Server Owner"
	grp.Privs = { "all" }
	grp.Hard = true
	grp.Rank = 255
	grp.Rstrict = {}
	self.UGS[ grp.ClassName ] = grp
end

function PLUG:GetDefaultUsergroup()
	local defdef = "restricted"
	if (SinglePlayer()) then defdef = "owner" end
	local ugname = plugs.Call( "GetServerValue", "DEF_UG", defdef )
	if (self:UserGroupExists( ugname )) then return ugname end
	return defdef
end

function PLUG:GetUsergroupData( name )
	// print( name )
	return self.UGS[ name ]
end

function PLUG:UserGroupExists( name )
	return !!self.UGS[ name or "" ]
end

function PLUG:GetUGS()
	return self.UGS
end

function PLUG:DataStreamSV( ply, tab )
	if (tab[1] == "applygroupprivs") then
		local ugname = tab[2]
		if (!ugname) then return self:Err( ply, "Invalid usergroup name!" ) end
		local changes = tab[3]
		if (!changes) then return self:Err( ply, "Invalid changes table!" ) end
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local g = self.UGS[ ugname ]
		if (!g) then return self:Err( ply, "Invalid usergroup!" ) end
		if (g.Hard) then return self:Err( ply, "Cannot delete that group!" ) end
		for k, v in pairs( changes ) do
			if (v) then
				plugs.Call( "AddToTable", g.Privs, k )
			else
				plugs.Call( "RemoveFromTable", g.Privs, k )
			end
		end
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Changed the privileges of '" .. ugname .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " changed the privileges of " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "Privileges of '" .. ugname .. "' changed!" )
		timer.Simple( 0.5, function()
			for _, pl in pairs( player.GetAll() ) do
				pl:BeamUGS( nil, ugname )
			end
		end )
	end
	if (tab[1] == "applygrouprstrict") then
		// PrintTable( tab )
		local ugname = tab[2]
		if (!ugname) then return self:Err( ply, "Invalid usergroup name!" ) end
		local changes = tab[3]
		if (!changes) then return self:Err( ply, "Invalid changes table!" ) end
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local g = self.UGS[ ugname ]
		if (!g) then return self:Err( ply, "Invalid usergroup!" ) end
		if (g.Hard) then return self:Err( ply, "Cannot delete that group!" ) end
		for k, v in pairs( changes ) do
			if (!v) then
				plugs.Call( "AddToTable", g.Rstrict, k )
			else
				plugs.Call( "RemoveFromTable", g.Rstrict, k )
			end
		end
		self.UGS[ ugname ] = g
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Changed the restrictions of '" .. ugname .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " changed the restrictions of " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "Restrictions of '" .. ugname .. "' changed!" )
		timer.Simple( 0.5, function()
			for _, pl in pairs( player.GetAll() ) do
				pl:BeamUGS( nil, ugname )
			end
		end )
	end
end

function PLUG:DoConCommand( ply, com, args )
	if (com == "getgrps") then
		ply.NextGGS = ply.NextGGS or 0
		if (CurTime() >= ply.NextGGS) then
			ply:BeamUGS()
			ply:PrintMessage( HUD_PRINTTALK, "Usergroups refreshed!" )
			ply.NextGGS = CurTime() + 2
		end
		return true
	end
	if (com == "grp_new") then
		if (!ply:HasPriv( "creategroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local base = args[1]
		if (!base) then return end
		table.remove( args, 1 )
		local name = table.concat( args, " " )
		if (!name) then return end
		if (name == "") then return self:Err( ply, "Please enter a name." ) end
		if (type( name ) != "string") then return self:Err( ply, "Please enter a valid name." )  end
		if (string.len( name ) < 2) then return self:Err( ply, "Name is too short!" ) end
		if (string.len( name ) > 16) then return self:Err( ply, "Name is too long!" ) end
		local int = string.lower( name )
		int = string.Replace( name, " ", "_" )
		int = string.Replace( name, ".", "" ) -- No directory haxing.
		int = string.Replace( name, "/", "" ) -- No directory haxing.
		int = string.Replace( name, "\\", "" ) -- No directory haxing.
		if (self.UGS[ int ]) then return self:Err( ply, "Group with same name already exists!" ) end
		local bsgrp = self.UGS[ base ] or {}
		local grp = {}
		grp.ClassName = int
		grp.PrintName = name
		grp.Privs = bsgrp.Privs or {}
		grp.Rank = bsgrp.Rank or 1
		grp.Rstrict = bsgrp.Rstrict or {}
		grp._FILENAME = GrpRoot .. grp.ClassName .. ".txt"
		self.UGS[ int ] = grp
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Created '" .. int .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " created group " .. name .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "Usergroup '" .. name .. "' created!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS()
		end
		return true
	end
	if (com == "grp_setrank") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot edit that group!" ) end
		local rank = args[2]
		if (!rank) then return self:Err( ply, "Invalid rank!" ) end
		local r = tonumber( rank )
		if (r > ply:GetRank()) then return self:Err( ply, "You cannot set rank above your own!" ) end
		self.UGS[ ugname ].Rank = r
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Set rank of '" .. ugname .. "' to " .. tostring( r ) )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " changed rank of " .. ugname .. " to " .. rank .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "Rank of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end
		return true
	end
	if (com == "grp_setcol") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot edit that group!" ) end
		local r, g, b, a = args[2], args[3], args[4], args[5]
		if (r == "none") then
			ply:Log( "usergroups", "Removed colour of '" .. ugname .. "'" )
			self.UGS[ ugname ].Colour = nil
		else
			if (r) then r = tonumber( r ) else return self:Err( ply, "Invalid colour!" ) end
			if (g) then g = tonumber( g ) else return self:Err( ply, "Invalid colour!" ) end
			if (b) then b = tonumber( b ) else return self:Err( ply, "Invalid colour!" ) end
			if (a) then a = tonumber( a ) else return self:Err( ply, "Invalid colour!" ) end
			local col = Color( r, g, b, a )
			self.UGS[ ugname ].Colour = col
			ply:Log( "usergroups", "Set colour of '" .. ugname .. "' to " .. r .. "," .. g .. "," .. b .. "," .. a )
		end
		plugs.Call( "SaveUsergroups" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " changed colour of " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "Colour of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end
		return true
	end
	if (com == "grp_givepriv") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot delete that group!" ) end
		local priv = args[2]
		if (!priv) then return self:Err( ply, "Invalid privilege!" ) end
		if (!plugs.Call( "IsValidPriv", priv )) then return self:Err( ply, "Invalid privilege!" ) end
		if (!ply:HasPriv( priv )) then return self:Err( ply, "You cannot add a privilege you don't have!" ) end
		if (table.HasValue( self.UGS[ ugname ].Privs, priv )) then return self:Err( ply, "Group already has that privilege!" ) end
		table.insert( self.UGS[ ugname ].Privs, priv )
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Gave '" .. priv .. "' to '" .. ugname .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " gave " .. priv .. " to " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "privileges of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end		
		return true
	end
	if (com == "grp_takepriv") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot edit that group!" ) end
		local priv = args[2]
		if (!priv) then return self:Err( ply, "Invalid privilege!" ) end
		if (!plugs.Call( "IsValidPriv", priv )) then return self:Err( ply, "Invalid privilege!" ) end
		if (!ply:HasPriv( priv )) then return self:Err( ply, "You cannot take a privilege you don't have!" ) end
		if (!table.HasValue( self.UGS[ ugname ].Privs, priv )) then return self:Err( ply, "Group doesn't have that privilege!" ) end
		for k, v in pairs( self.UGS[ ugname ].Privs ) do
			if (v == priv) then
				table.remove( self.UGS[ ugname ].Privs, k )
				break
			end
		end
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Took '" .. priv .. "' from '" .. ugname .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " took " .. priv .. " from " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "privileges of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end		
		return true
	end
	if (com == "grp_restrict") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot edit that group!" ) end
		local enttype = args[2]
		local classname = args[3]
		if (!enttype) then return self:Err( ply, "Invalid entity type!" ) end
		if (!classname) then return self:Err( ply, "Invalid class name!" ) end
		if ((enttype != "sent") && (enttype != "swep") && (enttype != "stool")) then return self:Err( ply, "Invalid entity type!" ) end
		local f
		for _, v in pairs( plugs.Call( "GetStoolSentSweps" ) or {} ) do
			if (v[1] == enttype) then
				if (v[2] == classname) then
					f = true
					break
				end
			end
		end
		if (!f) then return self:Err( ply, "Entity class not found!" ) end
		local grp = self.UGS[ ugname ]
		grp.Rstrict = grp.Rstrict or {}
		if (table.HasValue( grp.Rstrict, classname )) then return self:Err( ply, "Already restricted!" ) end
		table.insert( grp.Rstrict, classname )
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Restricted '" .. classname .. "' in '" .. ugname .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " restricted " .. classname .. " in " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "privileges of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end			
		return true
	end
	if (com == "grp_unrestrict") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot edit that group!" ) end
		local enttype = args[2]
		local classname = args[3]
		if (!enttype) then return self:Err( ply, "Invalid entity type!" ) end
		if (!classname) then return self:Err( ply, "Invalid class name!" ) end
		if ((enttype != "sent") && (enttype != "swep") && (enttype != "stool")) then return self:Err( ply, "Invalid entity type!" ) end
		local f
		for _, v in pairs( plugs.Call( "GetStoolSentSweps" ) or {} ) do
			if (v[1] == enttype) then
				if (v[2] == classname) then
					f = true
					break
				end
			end
		end
		if (!f) then return self:Err( ply, "Entity class not found!" ) end
		local grp = self.UGS[ ugname ]
		grp.Rstrict = grp.Rstrict or {}
		if (!table.HasValue( grp.Rstrict, classname )) then return self:Err( ply, "Not restricted!" ) end
		for k, v in pairs( grp.Rstrict ) do
			if (v == classname) then
				table.remove( grp.Rstrict, k )
				break
			end
		end
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Unrestricted '" .. classname .. "' in '" .. ugname .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " unrestricted " .. classname .. " in " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "privileges of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end			
		return true
	end
	if (com == "grp_delete") then
		if (!ply:HasPriv( "deletegroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot delete that group!" ) end
		//local fn = self.UGS[ ugname ]._FILENAME
		//if (file.Exists( fn )) then file.Delete( fn ) end
		plugs.Call( "DeleteUsergroup", ugname )
		ply:Log( "usergroups", "Deleted '" .. ugname .. "'" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " deleted group " .. ugname .. "!", true )
		self.UGS[ ugname ] = nil
		plugs.Call( "SaveUsergroups" )
		ply:PrintMessage( HUD_PRINTTALK, "Usergroup '" .. ugname .. "' deleted!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil )
		end
		return true
	end
	if (com == "grp_autopromote") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot edit that group!" ) end
		local g = args[2]
		local t = tonumber( args[3] or "0" )
		if (t < 1) then t = 1 end
		if (!self.UGS[ g ]) then return self:Err( ply, "Invalid usergroup!" ) end
		self.UGS[ ugname ].AutoPromoteGroup = g
		self.UGS[ ugname ].AutoPromoteTime = t * 60
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Set autopromote of " .. ugname .. " to " .. g .. ", after " .. t .. " mins" )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " changed auto promotion settings of " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "Auto promotion settings of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end
		return true
	end
	if (com == "grp_disableautopromote") then
		if (!ply:HasPriv( "editgroup" )) then return self:Err( ply, "You do not have enough privileges to do that!" ) end
		local ugname = args[1]
		if (!ugname) then return end
		if (!self.UGS[ ugname ]) then return self:Err( ply, "Invalid usergroup!" ) end
		if (self.UGS[ ugname ].Hard) then return self:Err( ply, "Cannot edit that group!" ) end
		self.UGS[ ugname ].AutoPromoteGroup = nil
		self.UGS[ ugname ].AutoPromoteTime = nil
		plugs.Call( "SaveUsergroups" )
		ply:Log( "usergroups", "Disabled auto promote for " .. ugname )
		plugs.Call( "SendChatNotice",  ply:Nick() .. " changed auto promotion settings of " .. ugname .. "!", true )
		ply:PrintMessage( HUD_PRINTTALK, "Auto promotion settings of '" .. ugname .. "' changed!" )
		for _, pl in pairs( player.GetAll() ) do
			pl:BeamUGS( nil, ugname )
		end
		return true
	end
end

function PLUG:Err( ply, text )
	ply:PrintMessage( HUD_PRINTTALK, text )
	return true
end